﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BallControl : MonoBehaviour {

    public static event System.Action OnReleaseArrow;

    public float dragRadious = 10f;
    public float launchSpeed = 30f;
    public const float gravity = -20f;
    public float bounceSpeed = 10f;
    public float bounceSpeedMin = .5f;
    public float bounceSpeedMax = 1f;
    public float bounceAngle = 20f;
    public float bounceAngleMin = .1f;
    public float bounceAngleMax = 1f;
    public GameObject arrow;
    public GameObject pathSphere;
    public bool isShowPath = false;
    public float drawPathSecond = 10f;
    public int drawPathresolution = 100;

    Rigidbody ball;
    Vector3 originPos;
    Camera _camera;
    Vector3 targetPos;
    Vector3 launchDir;
    bool isBallFlying = false;
    Rigidbody arrowRigidbody;
    List<GameObject> pathSpheres = new List<GameObject>();    

    void Start () {
        ball = GetComponent<Rigidbody>();
        _camera = Camera.main;
        originPos = transform.position;
        Physics.gravity = gravity * Vector3.up;
        ball.useGravity = false;

        InitialPathSpheres();
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            ResetBall();
        }
        if (Input.GetKey(KeyCode.LeftShift))
        {
            Debug.LogWarning(ball.velocity.magnitude);
            Debug.LogWarning(ball.velocity);
        }

        if (arrowRigidbody)
        {
            Vector3 arrowPoint = new Vector3(arrowRigidbody.velocity.x, arrowRigidbody.velocity.y, 0);
            Vector3 arrowPointDir = arrowPoint.normalized;
            float arrowAngle = Mathf.Atan2(arrowPointDir.x, arrowPointDir.y) * Mathf.Rad2Deg;
            //(90 - arrowAngle) make the transition from unity to the real world axis
            arrowRigidbody.transform.eulerAngles = Vector3.forward * (90 - arrowAngle);
        }

    }

    void OnMouseDrag()
    {
        if(isBallFlying == false)
        {
            AimTarget();
        }
    }

    private void OnMouseUp()
    {
        FireBall();
        ResetBall();
    }

    private void OnCollisionEnter(Collision collision)
    {
       if(collision.collider.tag == "Basketry")
        {
            Debug.Log("Hit the basketry");
        }
    }

    void FireBall()
    {
        isBallFlying = true;
        //ball.useGravity = true;
        //ball.velocity = launchDir * launchSpeed;
        arrowRigidbody = GameObject.Instantiate(arrow, ball.transform.position, Quaternion.identity).GetComponent<Rigidbody>();
        arrowRigidbody.velocity = launchDir * launchSpeed;
        StartCoroutine(Suicide(arrowRigidbody.gameObject)); 

    }

    void ResetBall()
    {
        transform.position = originPos;
        ball.useGravity = false;
        ball.velocity = Vector3.zero;
        isBallFlying = false;

        ClearPath();
    }

    void AimTarget()
    {
        
        Vector3 mouseScreenToWorld = _camera.ScreenToWorldPoint(Input.mousePosition);
        targetPos = new Vector3(mouseScreenToWorld.x, mouseScreenToWorld.y, originPos.z);

        if (DistanceXY(mouseScreenToWorld, originPos) >= dragRadious)
        {
            Vector3 dir = (new Vector3(mouseScreenToWorld.x, mouseScreenToWorld.y, originPos.z) - originPos).normalized;
            targetPos = dir * dragRadious + originPos;
        }
        transform.position = targetPos;
        launchDir = originPos - targetPos;

        if (isShowPath)
        {
            ShowPath();
        }
    }

    private float DistanceXY(Vector3 from,Vector3 to)
    {
        float result = Mathf.Sqrt(Mathf.Pow((from.x - to.x), 2) + Mathf.Pow((from.y - to.y), 2));
        return result;
    }

    private void OnDrawGizmos()
    {
        if (Application.isPlaying)
        {
            Gizmos.color = Color.red;
            Gizmos.DrawWireSphere(originPos, dragRadious);
        }
    }

    IEnumerator Suicide(GameObject obj)
    {
        //the ball(arrow) was moved by rigidbody,so i think use waitforfixedupdate might be more accuracy.
        WaitForFixedUpdate wait = new WaitForFixedUpdate();
        float halfWidth = GetCameraWidth() / 2;
        //for orthographic camera 
        float haflHeight = Camera.main.orthographicSize;
        while (true)
        {
            bool isOverScreenWidth = Mathf.Abs(obj.transform.position.x) > halfWidth;
            bool isLowerScreenHeight = obj.transform.position.y < -haflHeight;
            if (isOverScreenWidth || isLowerScreenHeight)
            {
                if (OnReleaseArrow != null)
                {
                    OnReleaseArrow();
                }
                GameObject.Destroy(obj);
                yield break;
            }else
            {
                yield return wait;
            }
        }
        
    }

    float GetCameraWidth()
    {
        Camera cam = Camera.main;
        float width = 2 * cam.orthographicSize * cam.pixelWidth / cam.pixelHeight;
        return width;
    }

    void InitialPathSpheres()
    {
        for (int i = 0; i < drawPathresolution; i++)
        {
            GameObject pathObj = GameObject.Instantiate(pathSphere, Vector3.zero, Quaternion.identity);
            pathObj.SetActive(false);
            pathSpheres.Add(pathObj);
            pathObj.transform.parent = this.transform;
        }
    }

    void ShowPath()
    {
        for (int i = 0; i < drawPathresolution; i++)
        {
            float simulationTime = i / (float)drawPathresolution * drawPathSecond;
            Vector3 displacement = launchDir * launchSpeed * simulationTime + Vector3.up * gravity * simulationTime * simulationTime / 2f;
            Vector3 drawPoint = ball.position + displacement;

            pathSpheres[i].transform.position = drawPoint;
            pathSpheres[i].SetActive(true);
        }
    }

    void ClearPath()
    {
        for (int i = 0; i < drawPathresolution; i++)
        {
            pathSpheres[i].SetActive(false);
        }
    }
}
